﻿using CMS.Dialogs.Notifications;
using CMS.Infos;
using CMS.REST;
using CMS.Util;
using Newtonsoft.Json.Linq;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CMS.Dialogs
{
    public partial class ImportExcelFileDialog : Form
    {

        public ImportExcelFileDialog()
        {
            InitializeComponent();
            TB_Path.Text = Utils.PrivateProfile.ReadString("Import", "Path", "");
        }

        private void Bn_Open_Click(object sender, EventArgs e)
        {
            if (FBD_Main.ShowDialog() == DialogResult.OK)
            {
                TB_Path.Text = FBD_Main.SelectedPath;
            }

        }

        private void TB_Path_TextChanged(object sender, EventArgs e)
        {
            CheckedListBox.ObjectCollection items = CLB_File.Items;
            items.Clear();
            if (Directory.Exists(TB_Path.Text))
            {
                items.AddRange((from f in Directory.GetFiles(TB_Path.Text, "*.xls?") select Path.GetFileName(f)).ToArray());
            }
        }

        private void Bn_SelectAll_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < CLB_File.Items.Count; i++)
            {
                CLB_File.SetItemChecked(i, true);
            }
        }

        private void Bn_SelectNone_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < CLB_File.Items.Count; i++)
            {
                CLB_File.SetItemChecked(i, false);
            }
        }



        private async void Bn_Start_Click(object sender, EventArgs e)
        {
            if ("导入中".Equals(Bn_Start.Text))
            {
                return;
            }
            if (!Directory.Exists(TB_Path.Text) || CLB_File.CheckedItems.Count == 0)
            {
                MessageBox.Show(this, "请选择文件进行导入", "警告");
                return;
            }

            Utils.PrivateProfile.WriteString("Import", "Path", TB_Path.Text);

            bool autoGen = CB_AutoGen.Checked;
            bool roomCourseType = RB_RoomType.Checked;  //是否为课室课表数据
            string path = TB_Path.Text;
            string[] files = CLB_File.CheckedItems.OfType<string>().ToArray();

            INoticePublisher publisher = ProcessNotificationDialog.Start($"导入{(roomCourseType ? "实验室" : "学生")}课表表格文件");

            try
            {
                Bn_Start.Text = "导入中";
                List<string> successFiles = new List<string>();
                await Task.Run(() =>
                {
                    if (roomCourseType)
                    {
                        RoomCourseExcel excel = new RoomCourseExcel();
                        if (excel.Init(publisher, 1))
                        {
                            foreach (string file in files)
                            {
                                if (excel.ImportRoomCourse(Path.Combine(path, file), autoGen))
                                {
                                    successFiles.Add(file);
                                }
                            }
                        }
                    }
                    else
                    {
                        RoomCourseExcel excel = new RoomCourseExcel();
                        if (excel.Init(publisher, 2))
                        {
                            foreach (string file in files)
                            {
                                if (excel.ImportAssistantCourse(Path.Combine(path, file), autoGen))
                                {
                                    successFiles.Add(file);
                                }
                            }
                        }
                    }
                });

                foreach (string file in successFiles)
                {
                    int index = CLB_File.Items.IndexOf(file);
                    if (index != -1)
                    {
                        CLB_File.SetItemChecked(index, false);
                    }
                }
                publisher.WriteLine("INFO", "已成功导入:\r\n" + string.Join("\r\n", successFiles));
            }
            finally
            {
                Bn_Start.Text = "开始导入";
            }
        }

        #region 表格导入

        public class RoomCourseExcel
        {
            /// <summary>
            /// 课室课表标题匹配
            /// </summary>
            private static readonly Regex TitleRegex = new Regex(@"北京理工大学珠海学院\s*(\S+)\s*教室课表");

            /// <summary>
            /// 课室课表详细信息匹配
            /// </summary>
            private static readonly Regex HeaderRegex = new Regex(@"学年学期:(\S+)\s+教室:(\S+)\s+所属功能区:(\S+)\s+所属教学楼:(\S+)\s+座位数:(\d+)");

            /// <summary>
            /// 课室课表课程信息匹配
            /// </summary>
            private static readonly Regex ItemRegex = new Regex(@"([^\n]+)\n([^\n]+)\n([^\n]+)\n\s*(\S+)周\[\s*(\S+)\s*节\](?:\n.*人数[:：](\d+))?");

            /// <summary>
            /// 学生课表标题匹配
            /// </summary>
            private static readonly Regex TitleRegex2 = new Regex(@"北京理工大学珠海学院\s*(\S+)\s*学生个人课表");

            /// <summary>
            /// 学生课表详细信息匹配
            /// </summary>
            private static readonly Regex HeaderRegex2 = new Regex(@"学年学期：(\S+)\s*班级：(\S+)\s*所属班级：(\S+)\s*学院：(\S+)\s*打印日期：(\S+)\s*");

            /// <summary>
            /// 学生课表课程信息匹配
            /// </summary>
            private static readonly Regex ItemRegex2 = new Regex(@"([^\n]+)\n([^\n]+)\n\s*(\S+)\[([单双])?周\]\s*(\S+)");



            /// <summary>
            /// 缓存已经读取的学期信息
            /// </summary>
            private List<Term> _Terms;

            /// <summary>
            /// 缓存已经读取的课室信息
            /// </summary>
            private List<Room> _Rooms;

            /// <summary>
            /// 缓存已经加载的助理信息
            /// </summary>
            private List<Assistant> _Assistant;

            /// <summary>
            /// 消息发布者,用于输出LOG
            /// </summary>
            private INoticePublisher _Publisher;

            /// <summary>
            /// 初始化此单元,此函数必须执行成功,两个导入函数才可以使用
            /// </summary>
            /// <param name="publisher"></param>
            /// <param name="_import">初始化导入课室课表的必要数据或导入学生课表的必要数据</param>
            /// <returns></returns>
            public bool Init(INoticePublisher publisher, int _import)
            {
                _Publisher = publisher;
                _Publisher?.WriteLine("INFO", "正在检查权限");
                if (!Permission.CheckPermission(Permission.ImportData))
                {
                    _Publisher?.WriteLine("ERROR", "当前登陆的用户可能没有权限导入课室课表,请尝试使用别的账号导入");
                    return false;
                }

                _Publisher?.WriteLine("INFO", "开始拉取学期信息");
                RESTResponse response = RESTClient.IndexManaged("apis/v1/terms");
                if (!response.IsSuccess)
                {
                    _Publisher?.WriteLine("ERROR", "无法同步最新的学期信息,导入失败");
                    return false;
                }
                _Terms = response.Data.ToObject<List<Term>>();

                switch (_import)
                {
                    case 1:
                        _Publisher?.WriteLine("INFO", "开始拉取课室信息");
                        response = RESTClient.IndexManaged("apis/v1/rooms");
                        if (!response.IsSuccess)
                        {
                            _Publisher?.WriteLine("ERROR", "无法同步最新的课室信息,导入失败");
                            return false;
                        }
                        _Rooms = response.Data.ToObject<List<Room>>();
                        break;
                    case 2:
                        _Publisher?.WriteLine("INFO", "开始拉取助理信息");
                        response = RESTClient.IndexManaged("apis/v1/assistants");
                        if (!response.IsSuccess)
                        {
                            _Publisher?.WriteLine("ERROR", "无法同步最新的助理名单,导入失败");
                            return false;
                        }
                        _Assistant = response.Data.ToObject<List<Assistant>>();
                        break;
                }

                return true;
            }

            /// <summary>
            /// 导入房间课表Excel文件
            /// </summary>
            public bool ImportRoomCourse(string file, bool autoGen)
            {
                RESTResponse response;
                _Publisher?.WriteLine("INFO", $"正在处理文件[{file}]");
                try
                {
                    bool isXlS = string.Compare(Path.GetExtension(file), ".xls", true) == 0;
                    using (FileStream fs = new FileStream(file, FileMode.Open))
                    {
                        IWorkbook workbook;
                        if (isXlS)
                        {
                            workbook = new HSSFWorkbook(fs);
                        }
                        else
                        {
                            workbook = new XSSFWorkbook(fs);
                        }

                        for (int index = 0; index < workbook.NumberOfSheets; index++)
                        {
                            ISheet sheet = workbook.GetSheetAt(index);
                            var tmpRow = sheet.GetRow(0);
                            if (tmpRow == null || tmpRow.GetCell(0) == null)
                            {
                                _Publisher?.WriteLine("WARN", $"跳过空白的Excel Sheet: [{sheet.SheetName}]\r\n");
                                continue;
                            }
                            if (!TitleRegex.IsMatch(tmpRow.GetCell(0).StringCellValue))
                            {
                                _Publisher?.WriteLine("WARN", $"跳过无效的Excel Sheet: [{sheet.SheetName}]\r\n");
                                continue;
                            }
                            _Publisher?.WriteLine("INFO", $"正在处理Excel Sheet:[{sheet.SheetName}]");
                            Match headerMatch = HeaderRegex.Match(sheet.GetRow(1).GetCell(1).StringCellValue);

                            if (headerMatch.Success)
                            {
                                string termName = headerMatch.Groups[1].Value;
                                string roomName = headerMatch.Groups[2].Value;
                                string buildingName = headerMatch.Groups[4].Value;
                                int seatCount = int.Parse(headerMatch.Groups[5].Value);

                                Term term = _Terms.Find(t => t.Name == termName);
                                if (term == null)
                                {
                                    if (autoGen)
                                    {
                                        response = RESTClient.SaveManaged("apis/v1/terms", new JObject()
                                        {
                                            {"name",termName }
                                        });
                                        if (response.IsSuccess)
                                        {
                                            _Publisher?.WriteLine("INFO", $"学期[{termName}]创建成功");
                                            term = new Term() { Name = termName, ID = response.Data[0].ToObject<int>() };
                                            _Terms.Add(term);
                                        }
                                        else
                                        {
                                            _Publisher?.WriteLine("WARN", $"学期[{termName}]未被创建,将跳过Excel Sheet:{sheet.SheetName}\r\n");
                                            continue;
                                        }
                                    }
                                    else
                                    {
                                        _Publisher?.WriteLine("WARN", $"学期[{termName}]未被创建,将跳过Excel Sheet:{sheet.SheetName}\r\n");
                                        continue;
                                    }
                                }

                                Room room = _Rooms.Find(r => r.Name == roomName);
                                if (room == null)
                                {
                                    if (autoGen)
                                    {
                                        response = RESTClient.SaveManaged("apis/v1/rooms", new JObject()
                                        {
                                            {"name",roomName },
                                            {"building",buildingName },
                                            {"seat_count",seatCount }
                                        });
                                        if (response.IsSuccess)
                                        {
                                            _Publisher?.WriteLine("INFO", $"课室[{roomName}]创建成功");
                                            room = new Room() { Name = roomName, Building = buildingName, SeatCount = seatCount };
                                            _Rooms.Add(room);
                                        }
                                        else
                                        {
                                            _Publisher?.WriteLine("WARN", $"课室[{roomName}]未被创建,将跳过Excel Sheet:{sheet.SheetName}\r\n");
                                            continue;
                                        }
                                    }
                                    else
                                    {
                                        _Publisher?.WriteLine("WARN", $"课室[{roomName}]未被创建,将跳过Excel Sheet:{sheet.SheetName}\r\n");
                                        continue;
                                    }
                                }

                                List<CourseRoom> courses = new List<CourseRoom>();

                                for (int i = 3; i < 10; i++)
                                {
                                    tmpRow = sheet.GetRow(i);
                                    if (tmpRow == null)
                                    {
                                        _Publisher?.WriteLine("WARN", $"在{sheet.SheetName}中跳过空白行");
                                        continue;
                                    }
                                    for (byte j = 1; j < 8; j++)
                                    {
                                        ICell tmpCell = tmpRow.GetCell(j);
                                        if (!string.IsNullOrWhiteSpace(tmpCell.StringCellValue))
                                        {
                                            MatchCollection itemMatchs = ItemRegex.Matches(tmpCell.StringCellValue);

                                            foreach (Match itemMatch in itemMatchs)
                                            {
                                                courses.Add(new CourseRoom()
                                                {
                                                    Name = itemMatch.Groups[1].Value?.Trim(),
                                                    ClassName = itemMatch.Groups[2].Value?.Trim(),
                                                    Teacher = itemMatch.Groups[3].Value?.Trim(),
                                                    ClassWeek = itemMatch.Groups[4].Value,
                                                    ClassTime = itemMatch.Groups[5].Value,
                                                    StudentCount = itemMatch.Groups[6].Success ? int.Parse(itemMatch.Groups[6].Value) : 0,
                                                    TermID = term.ID,
                                                    Week = j,
                                                    Room = roomName
                                                });
                                            }
                                        }

                                    }
                                }


                                CourseRoom[] distinctCourses = courses.Distinct().ToArray();
                                bool failure = false;

                                _Publisher?.WriteLine("INFO", $"已解析课室[{roomName}]的数据,共{distinctCourses.Length}条,正在检查冲突.");

                                for (int i = 1; i <= 7; i++)
                                {
                                    List<CourseRoom> crs = (from cr in distinctCourses where cr.Week == i select cr).ToList();
                                    while (crs.Count > 1)
                                    {
                                        CourseRoom cr = crs.First();
                                        crs.RemoveAt(0);
                                        CourseRoom cr3 = crs.Find(cr2 => (cr.ClassWeekRaw & cr2.ClassWeekRaw) != 0 && (cr.ClassTimeRaw & cr2.ClassTimeRaw) != 0 && cr.Week == cr2.Week);
                                        if (cr3 != null)
                                        {
                                            _Publisher?.WriteLine("ERROR", $"课程{cr}与{cr3}存在冲突,请解决冲突后重新提交");
                                            failure = true;
                                        }
                                    }
                                }

                                if (failure)
                                {
                                    _Publisher?.WriteLine("ERROR", $"课表[{sheet.SheetName}]自身存在冲突,即将跳过提交,请解决冲突后重新提交\r\n");
                                    continue;
                                }
                                else
                                {
                                    _Publisher?.WriteLine("INFO", "课表检查通过,即将提交.");
                                }

                                response = RESTClient.SaveListManaged("apis/v1/courses_room",
                                    JArray.FromObject(distinctCourses),
                                    new RESTWhere("term_id", term.ID),
                                    new RESTWhere("room", roomName)
                                    );
                                if (response.IsSuccess)
                                {
                                    _Publisher?.WriteLine("INFO", $"同步课室[{roomName}]的课程数据成功,共{distinctCourses.Length}条\r\n");
                                }
                                else
                                {
                                    _Publisher?.WriteLine("ERROR", $"同步课室[{roomName}]的课程数据失败,原因:{response.ErrMsg}\r\n");
                                }
                            }
                            else
                            {
                                _Publisher?.WriteLine("WARN", $"跳过无效的Excel Sheet:[{sheet.SheetName}]\r\n");
                                continue;
                            }
                        }
                    }

                    _Publisher?.WriteLine("INFO", $"文件[{file}]已处理完毕");
                    return true;
                }
                catch (Exception err)
                {
                    _Publisher?.WriteLine("INFO", "处理失败,原因:" + err.Message);
                    return false;
                }
            }

            /// <summary>
            /// 导入学生课表Excel文件
            /// </summary>
            /// <param name="file"></param>
            /// <param name="autoGen"></param>
            /// <returns></returns>
            public bool ImportAssistantCourse(string file, bool autoGen)
            {
                RESTResponse response;
                _Publisher?.WriteLine("INFO", $"正在处理文件[{file}]");
                try
                {
                    bool isXlS = string.Compare(Path.GetExtension(file), ".xls", true) == 0;
                    using (FileStream fs = new FileStream(file, FileMode.Open))
                    {
                        IWorkbook workbook;
                        if (isXlS)
                        {
                            workbook = new HSSFWorkbook(fs);
                        }
                        else
                        {
                            workbook = new XSSFWorkbook(fs);
                        }

                        for (int index = 0; index < workbook.NumberOfSheets; index++)
                        {
                            ISheet sheet = workbook.GetSheetAt(index);

                            Match titleMatch = TitleRegex2.Match(sheet.GetRow(0).GetCell(0).StringCellValue);
                            if (!titleMatch.Success)
                            {
                                _Publisher?.WriteLine("WARN", $"跳过无效的Excel Sheet: [{sheet.SheetName}]\r\n");
                                continue;
                            }
                            string assistantName = titleMatch.Groups[1].Value;

                            _Publisher?.WriteLine("INFO", $"正在处理Excel Sheet:[{sheet.SheetName}]");
                            Match headerMatch = HeaderRegex2.Match(sheet.GetRow(1).GetCell(0).StringCellValue);

                            if (headerMatch.Success)
                            {
                                string termName = headerMatch.Groups[1].Value;
                                string className = headerMatch.Groups[2].Value;
                                string collegeName = headerMatch.Groups[4].Value;
                                string sheetDate = headerMatch.Groups[5].Value;


                                Term term = _Terms.Find(t => t.Name == termName);
                                if (term == null)
                                {
                                    if (autoGen)
                                    {
                                        response = RESTClient.SaveManaged("apis/v1/terms", new JObject()
                                        {
                                            {"name",termName }
                                        });
                                        if (response.IsSuccess)
                                        {
                                            _Publisher?.WriteLine("INFO", $"学期[{termName}]创建成功");
                                            term = new Term() { Name = termName, ID = response.Data[0].ToObject<int>() };
                                            _Terms.Add(term);
                                        }
                                        else
                                        {
                                            _Publisher?.WriteLine("WARN", $"学期[{termName}]未被创建,将跳过Excel Sheet:{sheet.SheetName}\r\n");
                                            continue;
                                        }
                                    }
                                    else
                                    {
                                        _Publisher?.WriteLine("WARN", $"学期[{termName}]未被创建,将跳过Excel Sheet:{sheet.SheetName}\r\n");
                                        continue;
                                    }
                                }

                                Assistant assistant = _Assistant.Find(t => t.Name == assistantName);
                                if (assistant == null)
                                {
                                    if (autoGen)
                                    {
                                        response = RESTClient.SaveManaged("apis/v1/assistants", new JObject()
                                        {
                                            {"name",assistantName },
                                            {"class_name",className },
                                            {"college",collegeName }
                                        });
                                        if (response.IsSuccess)
                                        {
                                            _Publisher?.WriteLine("INFO", $"助理[{assistant}]创建成功");
                                            assistant = new Assistant() { Name = termName, ClassName = className, College = collegeName, ID = response.Data[0].ToObject<int>() };
                                            _Assistant.Add(assistant);
                                        }
                                        else
                                        {
                                            _Publisher?.WriteLine("WARN", $"助理[{assistantName}]未被创建,将跳过Excel Sheet:{sheet.SheetName}\r\n");
                                            continue;
                                        }
                                    }
                                    else
                                    {
                                        _Publisher?.WriteLine("WARN", $"助理[{assistantName}]未被创建,将跳过Excel Sheet:{sheet.SheetName}\r\n");
                                        continue;
                                    }
                                }

                                List<CourseAssistant> courses = new List<CourseAssistant>();

                                for (int i = 3; i < 10; i++)
                                {
                                    IRow tmpRow = sheet.GetRow(i);
                                    for (byte j = 1; j < 8; j++)
                                    {
                                        ICell tmpCell = tmpRow.GetCell(j);
                                        if (!string.IsNullOrWhiteSpace(tmpCell.StringCellValue))
                                        {
                                            MatchCollection itemMatchs = ItemRegex2.Matches(tmpCell.StringCellValue);

                                            foreach (Match itemMatch in itemMatchs)
                                            {
                                                courses.Add(new CourseAssistant()
                                                {
                                                    TermID = term.ID,
                                                    AssistantID = assistant.ID,
                                                    Week = j,
                                                    Course = itemMatch.Groups[1].Value?.Trim(),
                                                    ClassWeek = itemMatch.Groups[3].Value + itemMatch.Groups[4].Value,
                                                    ClassTimeRaw = 3u << ((i - 3) << 1),
                                                });
                                            }
                                        }
                                    }
                                }

                                CourseAssistant[] distinctCourses = courses.Distinct().ToArray();
                                bool failure = false;

                                _Publisher?.WriteLine("INFO", $"已解析助理室[{assistantName}]的数据,共{distinctCourses.Length}条,正在检查冲突.");

                                for (int i = 1; i <= 7; i++)
                                {
                                    List<CourseAssistant> crs = (from cr in distinctCourses where cr.Week == i select cr).ToList();
                                    while (crs.Count > 1)
                                    {
                                        CourseAssistant cr = crs.First();
                                        crs.RemoveAt(0);
                                        CourseAssistant cr3 = crs.Find(cr2 => (cr.ClassWeekRaw & cr2.ClassWeekRaw) != 0 && (cr.ClassTimeRaw & cr2.ClassTimeRaw) != 0 && cr.Week == cr2.Week);
                                        if (cr3 != null)
                                        {
                                            _Publisher?.WriteLine("ERROR", $"课程{cr}与{cr3}存在冲突,请解决冲突后重新提交");
                                            failure = true;
                                        }
                                        List<CourseAssistant> cr4 = crs.FindAll(cr2 => cr.ClassWeekRaw == cr2.ClassWeekRaw && cr.Course == cr2.Course && cr.Week == cr2.Week && cr.TermID == cr2.TermID);
                                        if (cr4.Count > 0)
                                        {
                                            cr3 = cr4.First();
                                            foreach (CourseAssistant item in cr4.Skip(1))
                                            {
                                                cr3.ClassTimeRaw |= item.ClassTimeRaw;
                                                crs.Remove(item);
                                            }
                                            _Publisher?.WriteLine("INFO", $"合并{cr4.Count}个项目");
                                        }
                                    }
                                }

                                if (failure)
                                {
                                    _Publisher?.WriteLine("ERROR", $"课表[{sheet.SheetName}]自身存在冲突,即将跳过提交,请解决冲突后重新提交\r\n");
                                    continue;
                                }
                                else
                                {
                                    _Publisher?.WriteLine("INFO", "课表检查通过,即将提交.");
                                }

                                response = RESTClient.SaveListManaged("apis/v1/courses_assistant",
                                    JArray.FromObject(distinctCourses),
                                    new RESTWhere("term_id", term.ID),
                                    new RESTWhere("assistant_id", assistant.ID)
                                    );
                                if (response.IsSuccess)
                                {
                                    _Publisher?.WriteLine("INFO", $"同步助理[{assistantName}]的课程数据成功,共{distinctCourses.Length}条\r\n");
                                }
                                else
                                {
                                    _Publisher?.WriteLine("ERROR", $"同步助理[{assistantName}]的课程数据失败,原因:{response.ErrMsg}\r\n");
                                }
                            }
                            else
                            {
                                _Publisher?.WriteLine("WARN", $"跳过无效的Excel Sheet:[{sheet.SheetName}]\r\n");
                                continue;
                            }
                        }
                    }

                    _Publisher?.WriteLine("INFO", $"文件[{file}]已处理完毕");
                    return true;
                }
                catch (Exception err)
                {
                    _Publisher?.WriteLine("INFO", "处理失败,原因:" + err.Message);
                    return false;
                }
            }
        }




        #endregion

    }
}
